package com.simplechain.util;

import com.simplechain.contract.Abi;
import com.simplechain.contract.AbiFunctionType;
import com.simplechain.contract.ContractParam;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.collect.Lists;

import org.apache.commons.codec.binary.Base64;
import org.apache.logging.log4j.util.Strings;
import org.web3j.abi.datatypes.Function;
import org.web3j.crypto.Keys;

import java.io.*;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.util.List;

public class Utils {

    public static String getResourceName(String path) {
        String[] sp = path.split("\\.");

        return sp[sp.length - 1];
    }

    public static Function convertFunction(String abiString, String name, String[] args) {
        List<Abi> abis = parseAbi(abiString);
        for (Abi abi : abis) {
            if (!abi.getName().equalsIgnoreCase(name)) {
                continue;
            }
            List<String> outs = Lists.newArrayListWithCapacity(abi.getOutputTypes().size());
            abi.getOutputTypes().forEach(type -> outs.add(type.getType()));
            List<AbiFunctionType> inputs = abi.getInputTypes();
            if (null == args || args.length == 0) {
                return ContractUtil.convertFunction(name, null, outs);
            }
            if (args.length != inputs.size()) {
                throw new RuntimeException("input args number not equals to abi");
            }
            List<ContractParam> params = Lists.newArrayListWithCapacity(inputs.size());
            for (int i = 0; i < inputs.size(); i++) {
                ContractParam param = new ContractParam();
                param.setType(inputs.get(i).getType());
                param.setValue(args[i]);
                params.add(param);
            }
            return ContractUtil.convertFunction(name, params, outs);
        }
        throw new RuntimeException("method name can't be found in abi");
    }

    public static List<Abi> parseAbi(String abi) {
        List<Abi> abis = Lists.newArrayList();
        ObjectMapper objectMapper = new ObjectMapper();
        try {
            JsonNode trees = objectMapper.readTree(abi);
            for (JsonNode tree : trees) {
                String type = tree.get("type").asText();
                if (!"function".equalsIgnoreCase(type)) {
                    continue;
                }
                Abi inner = new Abi();
                inner.setName(tree.get("name").asText());
                inner.setInputTypes(makeType(tree.get("inputs")));
                inner.setOutputTypes(makeType(tree.get("outputs")));
                abis.add(inner);
            }
            return abis;
        } catch (IOException e) {
            throw new RuntimeException("parse abi failed");
        }
    }

    public static List<AbiFunctionType> makeType(JsonNode node) {
        final List<AbiFunctionType> result = Lists.newArrayListWithCapacity(node.size());
        node.forEach(
                input -> {
                    AbiFunctionType type = new AbiFunctionType();
                    type.setType(input.get("type").asText());
                    type.setName(input.get("name").asText());
                    result.add(type);
                });
        return result;
    }

    public static String hexRemove0x(String hex) {
        if (hex.contains("0x")) {
            return hex.substring(2);
        }
        return hex;
    }

    public static String hexStr2Str(String s) {
        byte[] baKeyword = new byte[s.length() / 2];
        for (int i = 0; i < baKeyword.length; i++) {
            try {
                baKeyword[i] = (byte) (0xff & Integer.parseInt(s.substring(i * 2, i * 2 + 2), 16));
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        try {
            s = new String(baKeyword, StandardCharsets.UTF_8); // UTF-16le:Not
        } catch (Exception e1) {
            e1.printStackTrace();
        }
        return s;
    }

    public static byte[] longToBytes(long x) {
        ByteBuffer buffer = ByteBuffer.allocate(8);
        buffer.putLong(0, x);
        return buffer.array();
    }

    public static long bytesToLong(byte[] bytes) {
        ByteBuffer buffer = ByteBuffer.allocate(8);
        buffer.put(bytes, 0, bytes.length);
        buffer.flip();
        return buffer.getLong();
    }

    public static byte[] toByteArray(Object obj) {
        byte[] bytes = null;
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        try {
            ObjectOutputStream oos = new ObjectOutputStream(bos);
            oos.writeObject(obj);
            oos.flush();
            bytes = bos.toByteArray();
            oos.close();
            bos.close();
        } catch (IOException ex) {
            ex.printStackTrace();
        }
        return bytes;
    }


    public static String base64StrToHexStr(String base64Bytes) {
        byte[] b = Base64.decodeBase64(base64Bytes);
        //有时出现转换的公钥或者私钥第一位是多余的,去除
        if ((b.length == 33 || b.length == 65) && b[0] == 0) {
            byte[] bytes = new byte[b.length - 1];
            System.arraycopy(b, 1, bytes, 0, bytes.length);
            return bytesToHexStr(bytes);
        }
        return bytesToHexStr(b);
    }

    public static String bytesToHexStr(byte[] b) {
        StringBuffer result = new StringBuffer();
        for (int i = 0; i < b.length; i++) {
            if ((b[i] & 0xff) < 0x10)
                result.append("0");
            result.append(Long.toString(b[i] & 0xff, 16));
        }
        return result.toString().toLowerCase();
    }

    public static String publicKeyToEthAddress(byte[] publicKey) {
        String publicKeyHex = bytesToHexStr(publicKey);
        String address = Keys.getAddress(publicKeyHex);
        return "0x" + address;
    }

    public static String base64PublicKeyToEthAddress(String base64PublicKey) {
        String publicKeyHex = base64StrToHexStr(base64PublicKey);
        String address = Keys.getAddress(publicKeyHex);
        return "0x" + address;
    }

    private static void testAccount(String[] args) {
        String str = "Xe9KcsSJoeC1R0QIpzR8TEJvYLSwvwk7A69Xf/V1IBdluLQ4lmiLTopKdDCBycgEJiQX12SJ/d+44Z+vId8DVw==";
        String s = base64PublicKeyToEthAddress(str);
        System.out.println(s);
        System.out.println("**************************************************************************************************");
        System.out.println();


        str = "AI4grQqJ+DO8wpvJpUtvb4QodDGH5ng8NmQ4+Zuu5BQLubSK5f0jGhwS2ydNpmtG5/p48eE1pq1l+vUyQ8s7FKE=";
        s = base64PublicKeyToEthAddress(str);
        System.out.println(s);
        System.out.println("**************************************************************************************************");
        System.out.println();

    }

    //    将数据保存到文件
    public static void saveFile(String path, String content, boolean isAppend) {
        try {
            if(Strings.isBlank(path)){
                path = "/tmp/sipe-log.txt";
            }

            File file = new File(path);
            // if file doesnt exists, then create it
            if (!file.exists()) {
                file.createNewFile();
            }
            FileWriter fw = new FileWriter(file.getAbsoluteFile(), isAppend);
            BufferedWriter bw = new BufferedWriter(fw);
            bw.write(content);
            bw.write("\n\n");
            bw.flush();
            bw.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }


}
